/*
 * KKLayer.cpp
 *
 *  Created on: 2013-9-15
 *      Author: wanghua
 */

#include "KKLayer.h"
#include "KKDirector.h"
#include "XmlUtils.h"
#include <iostream>
#include <string.h>
#include "effects/CCGrid.h"

USING_NS_CC;

using namespace std;

KKLayer::KKLayer() {
	initLayerWithWidthHeight(0, 0);
}

KKLayer::~KKLayer() {
}

bool KKLayer::initLayerWithWidthHeight(int width, int height) {
	m_isUseLocalTouch = false;
	m_isStopFlingWhenTouch = true;

	m_width = width;
	m_height = height;

	m_vLimitSpace = 0;
	m_hLimitSpace = 0;

	m_limitScroll = true;
	m_hscroll = false;
	m_vscroll = false;

	m_bgImage = NULL;

	m_ccNode = CCLayer::create();

	m_ccNode->setAnchorPoint(ccp(0, 0));
	m_ccNode->ignoreAnchorPointForPosition(true);

	this->addChild(m_ccNode);

	this->setAnchorPoint(ccp(0, 0));

	this->ignoreAnchorPointForPosition(true);

	this->setContentSize(CCSize(width, height));

	return true;
}

KKLayer* KKLayer::layerWithWidthHeight(int width, int height) {

	KKLayer* layer = KKLayer::node();

	layer->m_width = width;
	layer->m_height = height;

	layer->setContentSize(CCSize(width, height));

	return layer;
}

KKLayer* KKLayer::layerWithFile(const char* path) {

	CCTexture2D* bg = CCTextureCache::sharedTextureCache()->addImage(path);

	KKLayer* layer = layerWithWidthHeight(bg->getContentSize().width,
			bg->getContentSize().height);

	layer->initWithFile(path);

	return layer;
}

bool KKLayer::initWithFile(const char* path) {

	this->m_image = path;

	CCSprite* bg = CCSprite::create(m_image.c_str());

	bg->setScaleX(this->getWidth() / bg->getContentSize().width);
	bg->setScaleY(this->getHeight() / bg->getContentSize().height);

	bg->setPosition(ccp(this->getWidth() / 2, this->getHeight() / 2));

	bg->setOpacity(this->getOpacity());

	m_bgImage = bg;

	this->addChild(bg, BG_IMAGE_ZORDER, BG_IMAGE_TAG);

	return true;
}

bool KKLayer::initCommonInfoWithXml(TiXmlElement* element, KKNode* parent) {

	if (KKClipableNode::initCommonInfoWithXml(element, parent)) {

		m_GestureDirector->setFlingEnabled(
				XmlUtils::getBoolAttr(element, "flingable", false));

		this->m_hscroll = XmlUtils::getBoolAttr(element, "hscroll", false);
		this->m_vscroll = XmlUtils::getBoolAttr(element, "vscroll", false);
		this->m_clipable = XmlUtils::getBoolAttr(element, "clipable", false);
		this->m_vLimitSpace = XmlUtils::getFloatAttr(element, "vspace", 100);
		this->m_hLimitSpace = XmlUtils::getFloatAttr(element, "hspace", 100);

		return true;
	}

	return false;
}

bool KKLayer::initDelayInfoWithXml(TiXmlElement* element) {

	if (KKClipableNode::initDelayInfoWithXml(element)) {

		m_ccNode->setContentSize(CCSize(m_maxX - m_minX, m_maxY - m_minY));

		if (!this->m_image.empty()) {
			initWithFile(this->m_image.c_str());
		}

		return true;
	}

	return false;
}

void KKLayer::resize(const float& width, const float& height) {
	KKNode::resize(width, height);
	if (this->m_bgImage) {

		m_ccNode->setContentSize(CCSize(m_maxX - m_minX, m_maxY - m_minY));

		m_bgImage->setScaleX(
				this->getWidth() / m_bgImage->getContentSize().width);
		m_bgImage->setScaleY(
				this->getHeight() / m_bgImage->getContentSize().height);

		m_bgImage->setPosition(ccp(this->getWidth() / 2, this->getHeight() / 2));
	}
}
void KKLayer::saveToXml(TiXmlElement* element) {

	element->SetAttribute("class", "Layer");

	KKClipableNode::saveToXml(element);

	CCNode* node = dynamic_cast<CCNode*>(this);

	element->SetDoubleAttribute("width", node->getContentSize().width);
	element->SetDoubleAttribute("height", node->getContentSize().height);

	element->SetDoubleAttribute("scalex", node->getScaleX());
	element->SetDoubleAttribute("scaley", node->getScaleY());

	element->SetAttribute("vscroll", m_vscroll ? "true" : "false");
	element->SetAttribute("hscroll", m_hscroll ? "true" : "false");
}

void KKLayer::setIsVisible(bool isVisible) {

	if (isVisible) {
		if (!this->m_isLoaded) {
			this->loadNode();
		}

		if (isVisible != this->getIsVisible()) {
			this->performEvent("onshow", NULL);
		}
	} else {
		if (isVisible != this->getIsVisible()) {
			this->performEvent("onhide", NULL);
		}
	}

	CCNode::setVisible(isVisible);

}

void KKLayer::onDown(const KKTouch& touch) {
	KKClipableNode::onDown(touch);
	if (m_isStopFlingWhenTouch) {
		m_ccNode->stopActionByTag(SMOOTHSCROLL_TAG);
	}

}

void KKLayer::onScroll(const KKTouch& touch, const CCPoint& scroll) {

	if (this->m_isDragable) {

		float dx = 0;

		float dy = 0;

		if (this->m_hscroll) {
			dx = -scroll.x;
		}

		if (this->m_vscroll) {
			dy = -scroll.y;
		}

		if (this->m_hscroll || this->m_vscroll) {
			scrollBy(dx, dy);
		}

		KKNode::onScroll(touch, scroll);

	}

}

#define VELOCITY_ACCELLENT 300.0

void KKLayer::onFling(const KKTouch& touch, const CCPoint& velocity) {

	if (this->m_hscroll) {

		float duration = abs(velocity.x / VELOCITY_ACCELLENT);

		if (duration > 3) {
			duration = 3;
		}

		float disX = velocity.x * duration / 2;

		if (KKDirector::isDebug) {
			CCLog("duration = %f,disX=%f,velocityX=%f\n", duration, disX,
					velocity.x);
		}

		smoothScrollBy(-disX, 0, duration);

	} else if (this->m_vscroll) {

		float duration = abs(velocity.y / VELOCITY_ACCELLENT);

		if (duration > 3) {
			duration = 3;
		}

		float disY = velocity.y * duration / 2;

		if (KKDirector::isDebug) {
			CCLog("duration = %f,disY=%f\n", duration, disY);
		}

		smoothScrollBy(0, -disY, duration);
	}

	KKNode::onFling(touch, velocity);
}

void KKLayer::scrollBy(float scrollX, float scrollY) {

	//CCLog("scroll by %f,%f,pos=%f,%f \n",scrollX,scrollY,this->getHScrollValue(),this->getVScrollValue());

	scrollTo(this->getHScrollValue() + scrollX,
			this->getVScrollValue() + scrollY);
}

void KKLayer::scrollTo(float scrollX, float scrollY) {

	//CCLog("scroll to %f,%f \n",scrollX,scrollY);

	if (this->m_hscroll) {

		if (m_limitScroll) {
			if (scrollX < -m_hLimitSpace) {
				scrollX = -m_hLimitSpace;
			} else if (scrollX > getHScrollMax() + m_hLimitSpace) {
				scrollX = getHScrollMax() + m_hLimitSpace;
			}
		}

		m_ccNode->setPositionX(-scrollX - this->m_minX);

		m_isMoved = true;

	}

	if (this->m_vscroll) {

		if (m_limitScroll) {
			if (scrollY < -m_vLimitSpace) {
				scrollY = -m_vLimitSpace;
			} else if (scrollY > getVScrollMax() + m_vLimitSpace) {
				scrollY = getVScrollMax() + m_vLimitSpace;
			}
		}

		m_isMoved = true;

		m_ccNode->setPositionY(-scrollY - this->m_minY);

	}
}

void KKLayer::stopSmoothScroll() {
	m_ccNode->stopActionByTag(SMOOTHSCROLL_TAG);
}

void KKLayer::smoothScrollBy(float scrollX, float scrollY, ccTime duration) {
	smoothScrollTo(this->getHScrollValue() + scrollX,
			this->getVScrollValue() + scrollY, duration);
}

#define SMOOTH_SPEED 333

void KKLayer::smoothScrollTo(float scrollX, float scrollY, ccTime duration) {

	if (KKDirector::isDebug) {
		CCLog("scroll to %f,%f \n", scrollX, scrollY);
	}

	m_ccNode->stopActionByTag(SMOOTHSCROLL_TAG);

	float x = m_ccNode->getPositionX();
	float y = m_ccNode->getPositionY();

	bool reset = false;

	if (this->m_hscroll) {

		//float speed = abs((scrollX-x)/duration);

		if (m_limitScroll) {
			if (scrollX < 0) {
				scrollX = 0;
				reset = true;
			} else if (scrollX > getHScrollMax()) {
				scrollX = getHScrollMax();
				reset = true;
			}
		}

		x = -scrollX - this->m_minX;

		//duration = abs((scrollX-x)/speed);

		if (reset && abs(x - m_ccNode->getPositionX()) <= m_hLimitSpace) {
			duration = abs(x - m_ccNode->getPositionX()) / SMOOTH_SPEED;
			if (duration > 0.6) {
				duration = 0.6;
			}
		}
	}

	if (this->m_vscroll) {

		if (m_limitScroll) {
			if (scrollY < 0) {
				scrollY = 0;
				reset = true;
			} else if (scrollY > getVScrollMax()) {
				scrollY = getVScrollMax();
				reset = true;
			}
		}

		y = -scrollY - this->m_minY;

		if (reset && abs(y - m_ccNode->getPositionY()) <= m_vLimitSpace) {
			duration = abs(y - m_ccNode->getPositionY()) / SMOOTH_SPEED;
			if (duration > 0.6) {
				duration = 0.6;
			}
		}

	}

	CCArray* actions = CCArray::createWithCapacity(2);

	actions->addObject(CCEaseIn::create(CCMoveTo::create(duration, ccp(x, y)), 0.5));

	actions->addObject(CCCallFunc::create(this,
	callfunc_selector(KKLayer::onSmoothScrollEnd)));

	CCAction* action = CCSequence::create(actions);

	action->setTag(SMOOTHSCROLL_TAG);

	m_ccNode->runAction(action);
}

void KKLayer::onSmoothScrollEnd() {

	CCRect bounds = m_ccNode->boundingBox();

	performEvent("onscrollend", NULL);

	CCLog("smooth scroll end\n");
}

float KKLayer::getHScrollMax() {
	return max(this->m_maxX - this->m_minX - this->getWidth(), 0.0f);
}

float KKLayer::getVScrollMax() {
	return max(this->m_maxY - this->m_minY - this->getHeight(), 0.0f);
}

float KKLayer::getHScrollValue() {
	return -(m_ccNode->getPositionX() + this->m_minX);
}

float KKLayer::getVScrollValue() {
	return -(m_ccNode->getPositionY() + this->m_minY);
}

void KKLayer::onEnter() {
	CCLayer::onEnter();
	this->performEvent("onenter", NULL);

	if (this->isPhysics() && this->m_body == NULL) {

		int group = this->getIntProperty("group", 1);

		KKScene* curScene = KKDirector::shareDirector()->getCurrentScene();
		curScene->createBody(this, group);

		m_body->SetActive(this->getIsVisible());
	}
}

void KKLayer::onExit() {
	CCLayer::onExit();
	this->performEvent("onexit", NULL);

	if (this->isPhysics() && this->m_body) {
		KKScene* curScene = KKDirector::shareDirector()->getCurrentScene();
		curScene->destroyBody(this);
	}
}

const ccColor3B& KKLayer::getColor() {
	return ccWHITE;
}

void KKLayer::visit() {

	int test;

	float box[5];

	if (!m_bVisible) {
		return;
	}
	glPushMatrix();

	/*if (m_pGrid && m_pGrid->isActive()) {
	 m_pGrid->beforeDraw();
	 this->transformAncestors();
	 }*/

	this->transform();

	KKClipableNode::beginClip(test, box);

	CCNode* pNode = NULL;
	unsigned int i = 0;

	if (m_pChildren && m_pChildren->count() > 0) {
		// draw children zOrder < 0
		ccArray *arrayData = m_pChildren->data;
		for (; i < arrayData->num; i++) {
			pNode = (CCNode*) arrayData->arr[i];

			if (pNode && pNode->getZOrder() < 0) {
				pNode->visit();
			} else {
				break;
			}
		}
	}

	// self draw
	this->draw();

	// draw children zOrder >= 0
	if (m_pChildren && m_pChildren->count() > 0) {
		ccArray *arrayData = m_pChildren->data;
		for (; i < arrayData->num; i++) {
			pNode = (CCNode*) arrayData->arr[i];
			if (pNode) {
				pNode->visit();
			}
		}
	}

	KKClipableNode::endClip(test, box);
	/*
	 if (m_pGrid && m_pGrid->isActive()) {
	 m_pGrid->afterDraw(this);
	 }*/

	glPopMatrix();

}

void KKLayer::setColor(const ccColor3B& color) {

	KKNodeArray::CCMutableArrayIterator it = m_objects->begin();

	CCRGBAProtocol* child = NULL;

	while (it != m_objects->end()) {
		child = dynamic_cast<CCRGBAProtocol*>(*it);

		if (child) {
			child->setColor(color);
		}
		it++;
	}

	if (this->m_bgColor) {
		this->m_bgColor->setColor(color);
	}

	if (this->m_bgImage) {
		this->m_bgImage->setColor(color);
	}
}

bool KKLayer::getIsOpacityModifyRGB() {
	return m_isOpacityModifyRGB;
}

void KKLayer::setIsOpacityModifyRGB(bool isOpacityModifyRGB) {
	m_isOpacityModifyRGB = isOpacityModifyRGB;

	KKNodeArray::CCMutableArrayIterator it = m_objects->begin();

	CCRGBAProtocol* child = NULL;

	while (it != m_objects->end()) {
		child = dynamic_cast<CCRGBAProtocol*>(*it);

		if (child) {
			child->setOpacityModifyRGB(isOpacityModifyRGB);
		}
		it++;
	}

	if (this->m_bgColor) {
		this->m_bgColor->setOpacityModifyRGB(isOpacityModifyRGB);
	}

	if (this->m_bgImage) {
		this->m_bgImage->setOpacityModifyRGB(isOpacityModifyRGB);
	}
}

GLubyte KKLayer::getOpacity() {
	return m_nOpacity;
}

void KKLayer::setOpacity(GLubyte opacity) {
	//	cout<<m_name<<" setOpacity "<<(int)opacity<<endl;
	this->m_nOpacity = opacity;

	KKNodeArray::CCMutableArrayIterator it = m_objects->begin();

	CCRGBAProtocol* child = NULL;

	while (it != m_objects->end()) {
		child = dynamic_cast<CCRGBAProtocol*>(*it);

		if (child) {
			child->setOpacity(opacity);
		}
		it++;
	}

	if (this->m_bgColor) {
		this->m_bgColor->setOpacity(opacity);
	}

	if (this->m_bgImage) {
		this->m_bgImage->setOpacity(opacity);
	}
}

bool KKLayer::layerContains(CCPoint touchPoint) {

	CCRect boundingBox = this->boundingBox();

	if (boundingBox.containsPoint(touchPoint)) {
		if (m_hasHotArea) {

			CCPoint localPoint = getLayerLocalPoint(touchPoint);

			assert(this->m_nodeVertexs && this->m_nodeVertexs->count() > 0);

			KKVertexArray* vertexes =
					this->m_nodeVertexs->getObjectAtIndex(0)->getVertexs();

			const KKPoint localPoinInArea(
					localPoint.x - getContentSize().width / 2,
					localPoint.y - getContentSize().height / 2);

			return isPointInPoly(*vertexes, localPoinInArea);

		}

		return true;

	}

	return false;

}

CCPoint KKLayer::getLayerLocalPoint(CCPoint point) {

	point = CCPointApplyAffineTransform(point, this->parentToNodeTransform());

	return point;
}

bool KKLayer::contains(const CCPoint& touchPoint) {

	return layerContains(touchPoint);
}

CCPoint KKLayer::getLocalTouchPoint(const CCPoint& touchPoint) {

	CCPoint point = CCPointApplyAffineTransform(touchPoint,
			this->parentToNodeTransform());

	CCPoint localPoint = CCPointApplyAffineTransform(point,
			m_ccNode->parentToNodeTransform());

	return localPoint;
}

